package com.example.util;

import com.example.annation.*;
import com.example.common.HttpHandlerModel;
import com.example.common.HttpServerStart;
import com.example.exection.InstanceManyExection;
import com.example.factory.HttpHandlerFactory;
import com.example.factory.MethodProxyFactory;
import com.example.factory.NewInstanceFactory;
import com.example.proxy.MethodProxy;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @Classname ApplicationContent
 * @Description TODO
 * @Date 2023/4/24 15:06
 * @Created by XiongXiong
 * @Author: X.I.O
 *
 * 注解扫描工厂
 */
public class ApplicationContent {

    private static final String CONFIG_FILE_NAME="config.properties";

    /**
     * 需要扫描的类
     */
    private static List<Class<?>> scanClassList;

    /**
     * 默认扫描的注解
     */
    private static List<Class<? extends Annotation>> defaultAnnationContent;

    /**
     * 需要扫描的注解
     */
    private static List<Class<? extends Annotation>> scanAnnotations=new ArrayList();

    /**
     * 只装载和扫描特定注解
     */
    private static Map<String,List<AnnotationMethodModel>> annotationModels;

    /**
     * 注解扫描生成的实例
     */
    private static Map<String,List<ApplicationModel>> classModels;

    /**
     * web方法代理
     */
    private static Map<String,MethodProxy> webMethodProxy;


    /**
     * 配置存储
     */
    private static Map<String,String> mapConfig;

    static {
        defaultAnnationContent=new ArrayList<>();
        defaultAnnationContent.add(Compontent.class);
        defaultAnnationContent.add(RestController.class);
        defaultAnnationContent.add(Service.class);
        defaultAnnationContent.add(Controller.class);
    }

    public static void start() throws IOException, ClassNotFoundException, IllegalAccessException, InstanceManyExection {
        List<Class<?>> classList=ClassScanUtils.getClassPaths();
        //先添加要扫描的注解
        for (Class< ? > cls : classList) {
            ScanCompontent scanCompontent=cls.getAnnotation(ScanCompontent.class);
            if(scanCompontent!=null){
                if(scanClassList==null){
                    scanClassList=new ArrayList<>();
                }
                Class<?>[] clsArr=scanCompontent.value();
                for (Class<?> s : clsArr) {
                    if(s.isAnnotation()){
                        ApplicationContent.scanClassList.add(s);
                    }
                }
            }
        }
        //添加需要新扫描的类
        if(ApplicationContent.scanClassList!=null){
            for (Class< ? > cls : scanClassList) {
                ClassScanUtils.searchClass(cls);
                ClassScanUtils.scanClass(cls);
            }
        }
        //重新获取需要扫描的类
        classList=ClassScanUtils.getClassPaths();

        //先实例化默认的注解
        for (Class< ? > cls : classList) {
            scanAllClassInAnnotations(cls);
        }

        //注入实例化注解对象数据
        for (Map.Entry<String,List<ApplicationModel>> modelMap:classModels.entrySet()){
            List<ApplicationModel> applicationModelList=modelMap.getValue();
            for (ApplicationModel applicationModel : applicationModelList) {
                Class<?> cls = applicationModel.getCls();
                Object obj=applicationModel.val;
                //属性注入
                Field[] fields=applicationModel.fields;
                for (int i = 0; i < fields.length ; i++) {
                    Field field = fields[i];
                    Autowired autowired=field.getAnnotation(Autowired.class);
                    if(autowired != null){
                        field.setAccessible(true);
                        Class<?> fieldClass=field.getType();
                        Object val=getObjByName(fieldClass.getName());
                        field.set(obj,val);
                    }
                }
                //web接口操作
                scanWebPathAnnotation(cls);
            }
        }

    }





    /**
     * 扫描web 接口
     * @param cls
     */
    private static void scanWebPathAnnotation(Class<?> cls) throws InstanceManyExection {
        RestController restController=cls.getAnnotation(RestController.class);
        if(restController==null){
            return;
        }
        Method[] methods=cls.getMethods();
        for (Method method : methods) {
            String path=containsSplit(restController.value());
            GetMapping getMapping=method.getAnnotation(GetMapping.class);
            PostMapping postMapping=method.getAnnotation(PostMapping.class);
            String requestMethod="";
            if(getMapping != null){
                path=path+containsSplit(getMapping.value());
                requestMethod="GET";
            }else if(postMapping!=null){
                path=path+containsSplit(postMapping.value());
                requestMethod="POST";
            }else {
                continue;
            }
            Object obj=getObjByName(cls.getName());
            MethodProxy methodProxy = MethodProxyFactory.create(cls, method,requestMethod,obj);
            if(ApplicationContent.webMethodProxy==null){
                ApplicationContent.webMethodProxy=new HashMap<>();
            }
            ApplicationContent.webMethodProxy.put(path,methodProxy);
        }
    }

    public static Map<String, MethodProxy> getWebMethodProxy() {
        return ApplicationContent.webMethodProxy;
    }


    /**
     * 扫描类上是否包含需要的注解
     * @param cls
     */
    private static void scanAllClassInAnnotations(Class<?> cls){
        //如果扫描的类是注解，不进行下去
        if(cls.isAnnotation()){
            return;
        }
        for(Class<? extends Annotation> scanClass: defaultAnnationContent ){
            Object annotation=cls.getAnnotation(scanClass);
            if(annotation != null){
                boolean isContains=isContainsNewInstance(cls);
                if(!isContains){
                    //扫描排序
                    Order order=cls.getAnnotation(Order.class);
                    Object obj=NewInstanceFactory.getObjectByClass(cls);
                    Class<?>[] clsArr=cls.getInterfaces();
                    int index=100;
                    if(order != null){
                        index=order.value();
                    }
                    ApplicationModel applicationModel=new ApplicationModel(cls,obj,index);
                    if(clsArr.length==0){
                        //如果不是接口
                        putNewInstance(cls,applicationModel);
                    }else {
                        //如果是接口
                        Class<?> c=clsArr[0];
                        putNewInstance(c.getName(),cls,applicationModel);
                    }
                }
            }
        }
    }

    public static Map<String,String> getConfig() throws IOException {
        if(mapConfig!=null){
            return mapConfig;
        }
        Properties properties=PropertiesUtils.load(CONFIG_FILE_NAME);
        Set<String> strings=properties.stringPropertyNames();
        if(mapConfig==null){
            mapConfig=new HashMap<String, String>();
        }
        for (String str:strings){
            String value=properties.getProperty(str);
            mapConfig.put(str,value);
        }
        return mapConfig;
    }

    private static String containsSplit(String path){
        if(path.startsWith("/")){
            return path;
        }
        return "/"+path;
    }

    public static void putNewInstance(Class<?> cls,ApplicationModel obj){
        putNewInstance(null,cls,obj);
    }

    public static void putNewInstance(String name,Class<?> cls,ApplicationModel obj){
        if(name==null){
            name=cls.getName();
        }
        if(classModels==null){
            classModels=new HashMap<>();
        }
        if(!classModels.containsKey(name)){
            classModels.put(name,new ArrayList<>());
        }
        classModels.get(name).add(obj);
    }

    //判断是否包含该实例
    public static boolean isContainsNewInstance(Class<?> cls){
        if(classModels==null){
            classModels = new HashMap<>();
            return false;
        }
        return classModels.containsKey(cls.getName());
    }

    public static <T> T getObj(Class<T> cls) throws InstanceManyExection {
        if(classModels==null){
            classModels=new HashMap<>();
        }
        List<ApplicationModel> appList=classModels.get(cls.getName());
        if(appList ==null){
            return null;
        }
        if(appList.size()==1){
            return (T) appList.get(0);
        }
        throw new InstanceManyExection(cls.getName()+"实例过多");
    }

    public static <T> List<T> getObjList(Class<T> cls){
        if(classModels==null){
            classModels=new HashMap<>();
        }
        List<ApplicationModel> appList=classModels.get(cls.getName());
        if(appList!=null){
            Collections.sort(appList, new Comparator<ApplicationModel>() {
                @Override
                public int compare(ApplicationModel o1, ApplicationModel o2) {
                    return o1.index - o2.index;
                }
            });
        }
        List<T> tList=null;
        tList=new ArrayList<>();
        if(appList!=null && appList.size() > 0){
            for (int i = 0; i < appList.size() ; i++) {
                ApplicationModel applicationModel=appList.get(i);
                T t=(T)applicationModel.getVal();
                tList.add(t);
            }
        }
        return tList;
    }

    public static Object getObjByCls(Class< ? > cls) throws InstanceManyExection {
        return getObjByName(cls.getName());
    }

    public static Object getObjByName(String name) throws InstanceManyExection {
        if(classModels==null){
            classModels=new HashMap<>();
        }
        List<ApplicationModel> appList=classModels.get(name);
        if(appList==null){
            return null;
        }
        if(appList.size() == 1){
            return appList.get(0).getVal();
        }
        throw new InstanceManyExection(name+"实例过多");
    }

    private static List<AnnotationMethodModel> getAnnotationModelsByName(Annotation annotation){
        String name=annotation.annotationType().getName();
        List<AnnotationMethodModel> annotationModelList=annotationModels.get(name);
        return annotationModelList;
    }


    public static void add(Annotation annotation,AnnotationMethodModel annotationModel){
        String name=annotation.annotationType().getName();
        if(annotationModel==null){
            annotationModels = new HashMap();
        }
        if(!annotationModels.containsKey(name)){
            annotationModels.put(name,new ArrayList<>());
        }
        annotationModels.get(name).add(annotationModel);
    }

    public static Map<String, List<ApplicationModel>> getClassModels() {
        return classModels;
    }

    class AnnotationMethodModel{

        /**
         * 实例类
         */
        private Class<?> cls;

        /**
         * 实例类名
         */
        private String name;

        /**
         * 注解拥有的信息
         */
        private Object annotation;

        /**
         * 注解实例类所在的方法
         */
        private Method method;

        /**
         * 注解所在实力类的属性
         */
        private Field field;
    }




    static class ApplicationModel{

        private Class<?> cls;

        private Object val;

        private Method[] methods;

        private Field[] fields;

        private int index=-1;

        public Class<?> getCls() {
            return cls;
        }

        public void setCls(Class<?> cls) {
            this.cls = cls;
        }

        public Object getVal() {
            return val;
        }

        public void setVal(Object val) {
            this.val = val;
        }

        public Method[] getMethods() {
            return methods;
        }

        public void setMethods(Method[] methods) {
            this.methods = methods;
        }

        public Field[] getFields() {
            return fields;
        }

        public void setFields(Field[] fields) {
            this.fields = fields;
        }

        public int getIndex() {
            return index;
        }

        public ApplicationModel(Class<?> cls, Object val,int index) {
            this.cls = cls;
            this.val = val;
            this.index = index;
            this.methods = this.cls.getDeclaredMethods();
            this.fields = this.cls.getDeclaredFields();
        }
    }
}
